﻿Imports SiNet.Main

Module Factory
    Dim theCaptchaID, theToken As String
    Dim theStreamArray As Array
    ''This is technically only required to get the proper stream in
    ''the "getLanding" function but I have set it on most all web requests
    Dim uaHeader As String = "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080208 Fedora/2.0.0.12-1 Firefox/2.0.0.12" & vbCrLf

    Public Function GenerateHash(ByVal SourceText As String) As String
        ''This is used to hash/encrypt the users password for storage
        Dim ByteSourceText() As Byte = Encoding.Default.GetBytes(SourceText)
        Dim Md5 As New MD5CryptoServiceProvider
        Dim ByteHash() As Byte = Md5.ComputeHash(ByteSourceText)
        Dim sb As New StringBuilder(32)
        For x As Integer = 0 To ByteHash.Length - 1
            If ByteHash(x) < 16 Then
                sb.Append("0")
            End If
            sb.Append(Convert.ToString(ByteHash(x), 16))
        Next
        Return sb.ToString
    End Function

    Private Function FindHTMLImg(ByVal sreachSrcString As String) As String
        Try
            ''Scrape out an image from the webbrowser control
            For Each element As HtmlElement In Browser.WebBrowserForm.Document.GetElementsByTagName("img")
                If element.GetAttribute("src").ToString.Contains(sreachSrcString) Then
                    Return element.GetAttribute("src").ToString.Trim()
                End If
            Next
            Return Nothing
        Catch ex As Exception
            Return Nothing
        End Try
        Return Nothing
    End Function

    Private Function FindFormValue(ByVal formInputName As String) As String
        Try
            ''Scrape out a value of a form from the webbrowser control
            For Each element As HtmlElement In Browser.WebBrowserForm.Document.GetElementsByTagName("input")
                If element.GetAttribute("name").ToString.Contains(formInputName) Then
                    Return element.GetAttribute("value").ToString.Trim()
                End If
            Next
            Return Nothing
        Catch ex As Exception
            MessageBox.Show("Crapped out finding form value!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return Nothing
        End Try
        Return Nothing
    End Function

    Private Function ParseChannels() As Integer
        Dim theStreamInfo As String = Nothing
        Try
            ''Scrapes out the info needed to formulate the URL to access a stations page
            'On Error Resume Next
            For Each element As HtmlElement In Browser.WebBrowserForm.Document.GetElementsByTagName("option")
                If Not element.GetAttribute("input").ToString.Equals("none") Then
                    ''take the string and parse out what we want (value)
                    ''Lets yank out the formatted stream name
                    Dim theGoodName As String = element.InnerText.ToString.Trim()
                    Dim values As Array = Split(element.GetAttribute("value").ToString, "|")
                    ''this has (in order) catagory-values(0), genre-values(1), stream name-values(2)
                    ''Grab some details about the channel from the xml document
                    If values.Length > 2 Then
                        ''If found, chanInfo returns, in order, NAME&NUMBER&DESCRIPTION&URL&LOGO&DETAILS
                        Dim chanInfo As Array = Split(getStationInfo(values(2)), "|")
                        ''Add the "good name" and channel number to the drop-down box
                        ''we use the chaninfo name for accessing the actual stream
                        If chanInfo.Length > 2 Then
                            If Not My.Settings.sinetSortByName Then
                                Main.ComboStreamBox.Items.Add(chanInfo(1) & " - " & theGoodName)
                            Else
                                Main.ComboStreamBox.Items.Add(theGoodName & " (" & chanInfo(1) & ")")
                            End If
                            theStreamInfo = theStreamInfo & "NAME=" & theGoodName & "&NUM=" & chanInfo(1) & "$activity=selectStream&category=" & values(0) & "&genre=" & values(1) & "&stream=" & values(2) & "|"
                        Else
                            ''If we end up here, we couldn't get station info from XML
                            ''Sorting is done on the combobox control
                            Main.ComboStreamBox.Items.Add(theGoodName)
                            theStreamInfo = theStreamInfo & "NAME=" & theGoodName & "$activity=selectStream&category=" & values(0) & "&genre=" & values(1) & "&stream=" & values(2) & "|"
                        End If
                    End If
                End If
            Next
            ''convert string to array for later use
            theStreamArray = Split(theStreamInfo, "|")
            Return 0
        Catch ex As Exception
            MessageBox.Show("Crapped out finding form value!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Private Function getCapToken() As String
        Try
            ''strip out token, captchaID
            theToken = FindFormValue("token")
            theCaptchaID = FindFormValue("captchaID")
            ''display captcha image
            CaptchaDialog.CaptchaBox.ImageLocation = FindHTMLImg("/mp/captcha/image/")
            ''prompt user to enter captcha code
            CaptchaDialog.TextBoxCaptchaCode.Focus()
            Return 0
        Catch ex As Exception
            MessageBox.Show("Crapped out getting Captcha and login token!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Public Function getURL(ByVal url As String, Optional ByVal specialReason As String = Nothing, _
                           Optional ByVal headers As String = Nothing, Optional ByVal strPost As String = Nothing) As String
        Try
            If specialReason = "login" Then
                Dim subClass As String = "subscriber"
                If My.Settings.sinetGuest Then
                    subClass = "guest"
                End If
                ''load the login page
                Browser.WebBrowserForm.Navigate(url, Nothing, Nothing, uaHeader)
                waitForBrowser(1, "login objects")
                ''check for error messages
                If checkBadMsg() <> 0 Then
                    Return 1
                End If
                ''TODO: CHECK TO SEE IF USER IS ALREADY LOGGED IN!!!
                ''now get the form info
                If getCapToken() <> 0 Then
                    Return 1
                End If
                Return 0
            ElseIf specialReason = "getstreams" Then
                If getStreams(url) <> 0 Then
                    Return 1
                End If
                Return 0
            Else
                ''if it's not for a specialReason, post to url with post headers and no post data
                Browser.WebBrowserForm.Navigate(url, Nothing, Nothing, uaHeader)
                waitForBrowser(1, "something")
                If checkBadMsg() <> 0 Then
                    Return 1
                End If
                Return 0
            End If
        Catch ex As Exception
            MessageBox.Show("ERROR! I could not get URL: " & url & vbNewLine & " Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Public Function doLogin() As String
        Try
            ''build entire string and submit
            Dim subClass As String = "subscriber"
            Dim passwdfield As String = "password"
            If My.Settings.sinetGuest Then
                subClass = "guest"
                passwdfield = "encryptedPassword"
            End If
            Dim PostData() As Byte = ASCIIEncoding.ASCII.GetBytes("activity=login&type=" & subClass & "&token=" & theToken & "&username=" & My.Settings.sinetUser & "&" & passwdfield & "=" & My.Settings.sinetPass & "&captchaID=" & theCaptchaID & "&captcha_response=" & Main.captchaCode & "&loginForm=" & subClass)
            Dim Headers As String = "Content-Type: application/x-www-form-urlencoded" & vbCrLf & "Referer: " & Main.theCountryURL & vbCrLf
            If PostData.Length > 0 Then
                Browser.WebBrowserForm.Navigate(Main.theCountryURL & "Login/" & subClass, Nothing, PostData, Headers & uaHeader)
                ''wait for browser to complete load before returning
                waitForBrowser(2, "login process")
                If checkBadMsg("login") <> 0 Then
                    Return 1
                End If
                Return 0
            End If
            Return 1
        Catch ex As Exception
            MessageBox.Show("Crapped out trying to login!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Public Function getStreams(ByVal url As String) As Integer
        Try
            ''get the new "I'm logged in" token
            writeLog(3, "Navigating to the small player...", False)
            waitForBrowser(5, "small player")
            If Browser.WebBrowserForm.Url.ToString.Contains("&token=") Then
                writeLog(4, "I found the token in the URL!", True)
            End If
            While Not Browser.WebBrowserForm.DocumentText.Contains("var playerType = " & Chr(34) & "small" & Chr(34))
                getURL(url)
                waitForBrowser()
                ''switch to minimized player
                Browser.WebBrowserForm.Navigate(url & "?activity=minimize", Nothing, Nothing, uaHeader)
                waitForBrowser(2, "stations")
                If checkBadMsg() <> 0 Then
                    Return 1
                End If
            End While
            writeLog(5, "done.", True)
            writeLog(3, "Parsing stations...", False)
            If Main.ComboStreamBox.Items.Count > 0 Then
                Main.ComboStreamBox.SelectedItem = Nothing
                Main.ComboStreamBox.Items.Clear()
            End If
            If ParseChannels() = 0 Then
                ''enable and sort the strem selection box and overlay
                Main.ComboStreamBox.Enabled = True
                Main.ComboStreamBox.Sorted = True
                Main.ComboStreamBox.Focus()
                Main.LabelComboOverLay.Visible = True
                writeLog(5, "done!", True)
                Return 0
            End If
            Return 1
        Catch ex As Exception
            MessageBox.Show("Crapped out trying to getStreams!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Public Function getLandingPage(ByVal url As String, ByVal streamName As String) As String
        Try
            ''search the array for a matching landing URL
            Dim block As String
            For Each block In theStreamArray
                If block.Contains(streamName) Then
                    Return url & "?" & block.Substring(block.IndexOf("$") + 1, (block.Length - (block.IndexOf("$") + 1))) & "&token=" & theToken & ""
                End If
            Next
        Catch ex As Exception
            writeLog(1, "Can't get landing page: " & ex.Message, False)
        End Try
        Return Nothing
    End Function

    Public Function getStreamDetails(ByVal url As String, ByVal station As String) As Integer
        Dim theJuice As String = Nothing
        waitForBrowser()
        Try
            ''Now grab the stream URL
            ''Note that in order to get a usable stream we have to have sent the MOZILLA HEADERS
            ''to the browser request that generated the input to this element
            For Each element As HtmlElement In Browser.WebBrowserForm.Document.GetElementsByTagName("EMBED")
                If element.GetAttribute("SRC").ToString.StartsWith("http://") Then
                    theJuice = element.GetAttribute("SRC").ToString.Trim()
                    writeLog(4, "Stream: " & theJuice, True)
                    Exit For
                End If
            Next
            ''Query the XML document
            Dim streamInfo As Array = Split(getStationInfo(getKey(station)), "|")
            If streamInfo.Length > 2 Then
                ''Grab the description from XML
                Main.TextBoxStationInfo.Text = streamInfo(2)
                ''Grab the station logo from XML
                Main.StreamImage.ImageLocation = streamInfo(4)
            Else
                ''No lookup found in XML, be somewhat polite
                Main.StreamImage.ImageLocation = Nothing
                Main.TextBoxStationInfo.Text = "Stream " & station & " Not Found In XML?"
            End If
            ''We got what we came for, now go back to the "not playing anything" page
            Browser.WebBrowserForm.Navigate(url & "?activity=minimize", Nothing, Nothing, uaHeader)
        Catch ex As Exception
            MessageBox.Show("Crapped out finding stream ASX!" & vbNewLine & "Error: " & ex.Message, _
                "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
        ''if we found a stream, play it
        If Not theJuice = Nothing Then
            Return initializePlayer(theJuice)
        End If
        Return 1
    End Function

    Private Function initializePlayer(ByVal streamASX As String) As Integer
        Try
            ''Enable media player controls
            Main.ButtonStop.Enabled = True
            Main.ButtonStop.Text = "Stop"
            Main.ButtonMute.Enabled = True
            Main.ButtonMute.Text = "Mute"
            Main.TrackBarVolume.Enabled = True
            ''Enable context menu controls
            Main.StopControlsContextMenuItem.Enabled = True
            Main.MuteControlsContextMenuItem.Enabled = True
            ''Fire up the media player
            writeLog(4, "Starting WMP...", False)
            Main.AxWindowsMediaPlayer1.Ctlcontrols.stop()
            Main.AxWindowsMediaPlayer1.settings.mute = False
            Main.AxWindowsMediaPlayer1.URL = streamASX
            Main.AxWindowsMediaPlayer1.Ctlcontrols.play()
            writeLog(5, "started.", True)

            ''No VLC support yet, as I have to write external song scrape script for song info
            ''Feed and activate the VLC control
            'writeLog(4, "Starting VLC...", False)
            'Main.AxVLCPlugin21.playlist.items.clear()
            'Main.AxVLCPlugin21.playlist.add(streamASX)
            'Main.AxVLCPlugin21.playlist.isPlaying.
            'Main.AxVLCPlugin21.playlist.play()
            'writeLog(5, "started.", True)
            Return 0
        Catch ex As Exception
            MessageBox.Show("Crapped out playing stream!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Private Function waitForBrowser(Optional ByVal times As Integer = 1, Optional ByVal textString As String = Nothing) As Integer
        Try
            Dim tr As Integer = 0
            While times > tr
                While Browser.WebBrowserForm.ReadyState <> WebBrowserReadyState.Complete
                    Main.ToolStripStatusLabel1.Text = "Loading..." & textString
                    Application.DoEvents()
                End While
                tr = (tr + 1)
            End While
            Main.ToolStripStatusLabel1.Text = "Loading..." & textString & " completed."
            Return 0
        Catch ex As Exception
            MessageBox.Show("Crapped out waiting for browser!" & vbNewLine & "Error: " & ex.Message, _
                            "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return 1
        End Try
    End Function

    Private Function checkBadMsg(Optional ByVal scope As String = Nothing) As Integer
        ''This function will scan the current page in the browser for error messages
        Dim webHTML As String = Browser.WebBrowserForm.DocumentText.ToLower
        If scope = "login" Then
            Select Case webHTML.Length
                Case webHTML.Contains("/mp/captcha/image/")
                    MessageBox.Show("Error detected, log-in failed without error? Click OK to see details.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Browser.Visible = True
                    Main.ShowBrowserToolStripMenuItem.Checked = True
                    Return 1
            End Select
        End If
        ''This checks for bad log-in info
        Select Case webHTML.Length > 0
            Case webHTML.Contains("the entered text does not match")
                ''captcha code is wrong (or missing)
                MessageBox.Show("You entered a bad captcha code, try again.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("sorry an error has occurred")
                ''waited too long to enter captcha
                MessageBox.Show("Generic error message, try again.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("password or username error")
                ''complete failure user, pass, and captch were entered
                MessageBox.Show("You entered a bad username or password, try again.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("please enter a password")
                ''user and captcha were entered
                MessageBox.Show("You failed to enter a password, set one and try again.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("please enter a username")
                ''password and captcha were entered
                MessageBox.Show("You failed to enter a username, set one and try again.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("unsuccessful login")
                ''general log-in failure
                MessageBox.Show("Your log-in failed for some unknown reason.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("logged in too many times today")
                ''logged in too many times today
                ''<td colspan="2"><img src="/mp/i/pg/pl/hdr_not_authorized.gif"></td>
                MessageBox.Show("You have exceeded your max daily log-ins. Try using the stop or mute buttons instead of closing SiNet.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return 1
            Case webHTML.Contains("/mp/i/unavailable/")
                ''This string should only come up on error pages
                ''Sorry_Pg3.gif is the "please try again later" message
                ''FindHTMLImg("/mp/i/unavailable/")
                MessageBox.Show("Error detected, but I don't recognise it, click OK to see details.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                ''Show the browser with the error
                Browser.Visible = True
                Main.ShowBrowserToolStripMenuItem.Checked = True
                Return 1
            Case webHTML.Contains("<body class=" & Chr(34) & "logout" & Chr(34))
                ''this is the body class when you get logged out
                MessageBox.Show("You appear to have been logged out, click OK to see details.", "- SiNet Error -", MessageBoxButtons.OK, MessageBoxIcon.Error)
                ''Show the browser with the error
                Browser.Visible = True
                Main.ShowBrowserToolStripMenuItem.Checked = True
                Return 1
        End Select
        Return 0
    End Function

    Public Function writeLog(ByVal errLevel As Integer, ByVal message As String, ByVal newLine As String) As Integer
        Dim tag As String = Nothing
        Select Case errLevel
            Case errLevel = 1
                tag = "FATAL: "
            Case errLevel = 2
                tag = "WARN: "
            Case errLevel = 3
                tag = "DEBUG: "
            Case errLevel = 4
                tag = "INFO: "
            Case errLevel = 5
                tag = Nothing
        End Select
        If newLine Then
            newLine = vbNewLine
        Else
            newLine = ""
        End If
        AppLogForm.TextBoxWebInfo.AppendText(tag & message & newLine)
        Return 0
    End Function

    Public Function IsConnectionAvailable() As Boolean
        Dim objUrl As New System.Uri("http://www.google.com/")
        Dim objWebReq As System.Net.WebRequest
        objWebReq = System.Net.WebRequest.Create(objUrl)
        Dim objResp As System.Net.WebResponse
        Try
            ''Let's attempt to get response and return True
            objResp = objWebReq.GetResponse
            objResp.Close()
            objWebReq = Nothing
            Return True
        Catch ex As Exception
            objWebReq = Nothing
            Return False
        End Try
    End Function

    Public Function getStationInfo(ByVal station As String) As String
        Dim allInfo As String = Nothing
        ''This has everything you ever wanted to know about every station available
        ''http://www.sirius.com/servlet/ContentServer?pagename=Sirius/XML/ChannelLineupXML&c=ChannelLineup&cid=1187814019390
        ''But was tweaked to contain the short description, logo URL, and other tag changes
        Dim localXML As String = Application.StartupPath & "\" & "station_info.xml"
        If Not File.Exists(localXML) Then
            Return ""
        End If
        Dim Doc As New XmlDocument
        Doc.Load(localXML)
        Dim Nodes As XmlNodeList = Doc.SelectNodes("/lineup/category/genre/channel[@key=" & Chr(34) & getKey(station) & Chr(34) & " and @sirflag=" & Chr(34) & "Yes" & Chr(34) & "]/*")
        Dim Node As XmlNode
        For Each Node In Nodes
            If Node.Name.Equals("station_name") Then
                allInfo = Node.InnerText
            ElseIf Node.Name.Equals("number") Then
                If Node.InnerText.Length > 3 Then
                    allInfo = allInfo & "|000"
                Else
                    If Node.InnerText.Length = 1 Then
                        ''make the channel 3 digits
                        allInfo = allInfo & "|00" & Node.InnerText
                    ElseIf Node.InnerText.Length = 2 Then
                        allInfo = allInfo & "|0" & Node.InnerText
                    Else
                        allInfo = allInfo & "|" & Node.InnerText
                    End If
                End If
            ElseIf Node.Name.Equals("description") Then
                allInfo = allInfo & "|" & Node.InnerText
            ElseIf Node.Name.Equals("url") Then
                allInfo = allInfo & "|" & Node.InnerText
            ElseIf Node.Name.Equals("logo") Then
                allInfo = allInfo & "|" & Node.InnerText
            ElseIf Node.Name.Equals("youwillhear") Then
                allInfo = allInfo & "|" & Node.InnerText
            End If
        Next
anError:
        ''Returns NAME|NUMBER|DESCRIPTION|URL|LOGO|DETAILS or Nothing
        Return allInfo
    End Function

    Private Function getKey(ByVal station As String) As String
        ''This fixes any quirks in the station name
        If station.ToLower = "familynetradio" Then
            Return "christiantalk"
        ElseIf station.ToLower = "howard100" Then
            Return "howardstern100"
        ElseIf station.ToLower = "howard101" Then
            Return "howardstern101"
        ElseIf station.ToLower = "thecoffeehouse" Then
            Return "coffeehouse"
        ElseIf station.ToLower = "heart&soul" Then
            Return "heartandsoul"
        ElseIf station.ToLower = "thegratefuldeadchannel" Then
            Return "gratefuldead"
        ElseIf station.ToLower = "metropolitanoperaradio" Then
            Return "metropolitanopera"
        ElseIf station.ToLower = "cosmoradio" Then
            Return "cosmopolitanradio"
        ElseIf station.ToLower = "worldradionetwork" Then
            Return "wrn"
        ElseIf station.ToLower = "bbcworldservicenews" Then
            Return "bbcworld"
        ElseIf station.ToLower = "ewtnglobalcatholicnetwork" Then
            Return "ewtnglobal"
        ElseIf station.ToLower = "lithium" Then
            Return "90salternative"
        ElseIf station.ToLower = "iceberg95" Then
            Return "icebergradio"
        ElseIf station.ToLower = "canadianweathernetwork" Then
            Return "canadaweather"
        End If
        Return station.Replace("'", "").Replace(" ", "").Replace("_", "").Replace("-", "").ToLower
    End Function

End Module